// 泛型（Generics）是指在定义函数，接口或类的时候，不预先指定具体的类型，而在使用的时候，再指定类型的一种特性。
interface lengthwise {
    length: number;
}

function getLength<T extends lengthwise>(arg: T): T {
    console.log(arg.length);
    return arg;
}

// 传入的值必须有length 属性
console.log(getLength({ length: 123 }));
getLength('zs');
// getLength(123) // error

// function createArray(length: number, value: any): Array<any> {
//   let result = [];
//   for (let i = 0; i < length; i++) {
//       result[i] = value;
//   }
//   return result
// }
// createArray(3, 'x'); // ['x', 'x', 'x']

// function createArray<T>(length: number, value: T): Array<T> {
//   let result = [];
//   for (let i = 0; i < length; i++) {
//     result[i] = value;
//   }
//   return result
// }
// console.log(createArray(3, 'zs'));

// 多个类型参数
// 定义泛型的时候，可以一次定义多个类型参数：
function swap<T, U>(tuple: [T, U]): [U, T] {
    return [tuple[1], tuple[0]];
}
console.log(swap([7, 'seven']));

// 泛型约束
// 在函数内部使用泛型变量的时候，由于事先不知道它是哪种类型，所以不能随意的操作他的属性或者方法：
// function loggingIdentity<T>(arg: T): T {
//   console.log(arg.length) // error
//   return arg
// }
// 这时，我们可以对泛型进行约束，只允许这个函数传入的那些包含length的变量，这就是泛型约束
interface Lengthwise {
    length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);
    return arg;
}
// console.log(loggingIdentity(7)); // error
console.log(loggingIdentity('zs'));
console.log(loggingIdentity({ length: 123 }));

// 多个参数之前也可以相互约束：**
function copyFields<T extends U, U>(target: T, source: U): T {
    for (let k in source) {
        target[k] = (<T>source)[k];
    }
    return target;
}

let x = { a: 1, b: 2, c: 3 };
console.log(copyFields(x, { a: 10 }));

// 可以使用接口的方式来定义一个函数需要符合的形状：
interface SearchFunc {
    (source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function (source, subString) {
    return source.search(subString) !== -1;
};

// 当然后可以使用含有泛型的接口来定义函数的形状：
interface CreateArrayFunc {
    <T>(length: number, value: T): T[];
}
let createArray: CreateArrayFunc;
createArray = function (length, value) {
    let result = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
};

console.log(createArray(3, 'ls'));

// 泛型类
// 与泛型接口类型，泛型也可以用于类的类型定义中：
class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function (x, y) {
    return x + y;
};
console.log(myGenericNumber.add(12, 13));

// 泛型参数的默认类型
// 默认类型为 string, 参数也可以传入其他数据类型的
function createArray1<T = string>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}
console.log(createArray1(10, 'zs'));
